package com.demo.tx.seata.service.tcc.impl;

import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.demo.tx.seata.dao.mapper.UserMapper;
import com.demo.tx.seata.model.entity.User;
import com.demo.tx.seata.service.tcc.SeataTCCService;
import io.seata.rm.tcc.api.BusinessActionContext;
import io.seata.rm.tcc.api.BusinessActionContextParameter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.util.concurrent.TimeUnit;

@Slf4j
@Service
public class SeataTCCServiceImpl extends ServiceImpl<UserMapper, User> implements SeataTCCService {

    /**
     * BusinessActionContext调用方可以传null，TCC模式下调prepare方法调用会进行拦截ActionInterceptorHandler.proceed()。
     * 如果为null会创建一个BusinessActionContext对象。
     * BusinessActionContextParameter注解必须写在实现类的方法参数上，这样才可以把参数传递到BusinessActionContext对象里
     *
     * @param userId 用户id
     * @param sleepTime 模拟业务处理时间
     */
    @Override
    public void prepare(BusinessActionContext actionContext,
                        @BusinessActionContextParameter(paramName = "userId") Long userId,
                        @BusinessActionContextParameter(paramName = "sleepTime") Long  sleepTime) throws InterruptedException {
        User user = new User().setUserName("tcc-tx-" + sleepTime).setUserId(userId);
        this.save(user);

        if (sleepTime > 10) {
            throw new RuntimeException("sleepTime > 10");
        }

        log.info("业务处理中");
        TimeUnit.SECONDS.sleep(sleepTime);
    }

    @Override
    public boolean commitMethod(BusinessActionContext actionContext) {
        long currentedTimeMillis = System.currentTimeMillis();
        log.info("用户 {}, 更新创建时间 {}", actionContext.getActionContext("userId"), currentedTimeMillis);

        LambdaUpdateWrapper<User> wrapper = new LambdaUpdateWrapper<>();
        wrapper.set(User::getCreateTime, currentedTimeMillis);
        wrapper.eq(User::getUserId, actionContext.getActionContext("userId"));
        this.update(wrapper);
        return true;
    }

    @Override
    public boolean rollbackMethod(BusinessActionContext parameterContext) {
        Integer userId = (Integer) parameterContext.getActionContext("userId");
        log.info("用户 {} 回滚", userId);
        LambdaUpdateWrapper<User> wrapper = new LambdaUpdateWrapper<>();
        wrapper.set(User::getUserName, "tcc-tx-rollback-" + parameterContext.getActionContext("sleepTime"));
        wrapper.eq(User::getUserId, userId);
        this.update(wrapper);
        return true;
    }
}
